/*Log.hpp*/
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fstream>
#include <mutex>
#include <pthread.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <time.h>
#include <sstream>
#include <filesystem>
#include <signal.h>
#include <sys/wait.h>
#define PATHNAME "./tmp"
#define PROID 123
#define CREATEMSGQUEUE (IPC_CREAT | IPC_EXCL | 0666)
#define GETMSGQUEUE (IPC_CREAT)
#define CLIENT_MSG_TYPE 1
#define SERVER_MSG_TYPE 2
#define INF 0
#define DBG 1
#define ERR 2
#define DEFAULT_LOG_LEVEL INF
#define LOG_MSG(level, format, ...)                                                         \
    do                                                                                      \
    {                                                                                       \
        if (DEFAULT_LOG_LEVEL > level)                                                      \
            break;                                                                          \
        time_t t = time(NULL);                                                              \
        struct tm *lt = localtime(&t);                                                      \
        char buf[32] = {0};                                                                 \
        strftime(buf, 31, "%H:%M:%S", lt);                                                  \
        fprintf(stdout, "[%s %s:%d] " format "\n", buf, __FILE__, __LINE__, ##__VA_ARGS__); \
    } while (0)
#define ILOG(format, ...) LOG_MSG(INF, format, ##__VA_ARGS__)
#define DLOG(format, ...) LOG_MSG(DBG, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG_MSG(ERR, format, ##__VA_ARGS__)

namespace ns_log
{
    const std::string default_filepath = "./Project/log/";
    const std::string default_filename = "This_project.log";
    const int default_maxline = 1000; //日志文件行数限制
    const int default_fd = -1;
    const int default_size = 1024; // 1k
    // 责任链模式
    static std::string delfile;
    // 责任链基类
    class Handler
    {
    public:
        virtual ~Handler() {}
        virtual void Execute(const std::string &text) = 0;
        void SetNext(std::shared_ptr<Handler> next)
        {
            _next = next;
        }
        void Enable()
        {
            _isEnable = true;
        }
        void Disable()
        {
            _isEnable = false;
        }
        virtual std::string GetCurrTime()
        {
            time_t now_time = time(nullptr);
            struct tm *curr_time = localtime(&now_time);
            // 转换为string格式
            char buf[128];
            snprintf(buf, sizeof(buf), "[%d-%02d-%02d %02d:%02d:%02d][%d]",
                     curr_time->tm_year + 1900,
                     curr_time->tm_mon + 1,
                     curr_time->tm_mday,
                     curr_time->tm_hour,
                     curr_time->tm_min,
                     curr_time->tm_sec,
                     getpid());
            return buf;
        }

    protected:
        std::shared_ptr<Handler> _next;
        bool _isEnable = true;
    };

    // 保存文件节点
    class HandleSaveFile : public Handler
    {
    private:
        std::string _filepath;
        std::string _filename;

    public:
        HandleSaveFile(const std::string &filepath = default_filepath, const std::string &filename = default_filename)
            : _filepath(filepath),
              _filename(filename)
        {
            if (std::filesystem::exists(_filepath))
                return;
            // 目录不存在需要创建
            CreateFilepath();
        }
        void CreateFilepath()
        {
            try
            {
                std::filesystem::create_directories(_filepath);
            }
            catch (std::filesystem::filesystem_error &error)
            {
                ELOG("%s\n", error.what());
                abort();
            }
        }
        void Execute(const std::string &text) override
        {
            std::string file = _filepath + _filename;
            if (_isEnable)
            {
                // 保存到文件
                std::ofstream ofs(file, std::ios::app);
                if (!ofs.is_open())
                {
                    ELOG("%s打开失败\n", file.c_str());
                    return;
                }
                ofs << text;
                ofs.close();
                ILOG("保存到文件done\n");
            }
            if (_next)
            {
                _next->Execute(text);
            }
            else
            {
                DLOG("到达责任链结尾，本次任务处理完毕\n");
            }
        }
    };
    // 检测当前日志文件是否过大
    class HandleBackUp : public Handler
    {
    private:
        std::string _filepath;
        std::string _filename;
        int _maxline;

    public:
        HandleBackUp(const std::string &filepath = default_filepath,
                     const std::string &filename = default_filename,
                     int maxline = default_maxline)
            : _filepath(filepath),
              _filename(filename),
              _maxline(maxline)
        {
        }
        void Execute(const std::string &text) override
        {
            if (_isEnable)
            {
                std::string file = _filepath + _filename;
                if (IsOutRange(file))
                {
                    // 分片保存,让子进程去做这件事！
                    BackUp(file);
                }
                ILOG("分片检测处理完毕\n");
            }
            if (_next)
            {
                _next->Execute(text);
            }
            else
            {
                DLOG("到达责任链结尾，本次任务处理完毕\n");
            }
            return;
        }

    public:
        virtual std::string GetCurrTime() override
        {
            time_t now_time = time(nullptr);
            struct tm *curr_time = localtime(&now_time);
            // 转换为string格式
            char buf[128];
            snprintf(buf, sizeof(buf), ".%d_%02d_%02d_%02d_%02d_%02d_%d",
                     curr_time->tm_year + 1900,
                     curr_time->tm_mon + 1,
                     curr_time->tm_mday,
                     curr_time->tm_hour,
                     curr_time->tm_min,
                     curr_time->tm_sec,
                     getpid());
            return buf;
        }

    private:
        void BackUp(const std::string &file)
        {
            std::string suffix = GetCurrTime();
            std::string backfilename = file + "." + suffix;
            printf("文件:%s\n", backfilename.c_str());
            delfile = backfilename;
            // 注意：不能给打包文件带目录路径，即./tmp/test.log.xxx 不能携带./tmp/,只保留test.log.xxx
            std::string tgzfilesrc = _filename + "." + suffix;
            std::string tgzfiledst = tgzfilesrc + ".tgz";

            // 1.创建子进程
            pid_t pid = fork();
            if (pid == 0)
            {
                // 2.先将文件重命名
                std::filesystem::rename(file, backfilename);
                // 3.让子进程执行进程替换创建tgz格式文件备份
                std::filesystem::current_path(_filepath);
                // 进程替换执行打包命令后删除备份文件
                execlp("tar", "tar", "-czf", tgzfiledst.c_str(), tgzfilesrc.c_str(), nullptr);
                exit(1);
            }
        }
        bool IsOutRange(const std::string &file)
        {
            std::ifstream ifs(file);
            if (!ifs.is_open())
            {
                return false;
            }
            int lines = 0;
            std::string line;
            while (std::getline(ifs, line))
            {
                ++lines;
            }
            ifs.close();
            return lines > _maxline;
        }
    };

    // 责任链入口
    class HandlerEntry
    {
    private:
        std::shared_ptr<HandleSaveFile> _save;
        std::shared_ptr<HandleBackUp> _backup;

    public:
        HandlerEntry()
        {
            // 构造责任链节点
            _save = std::make_shared<HandleSaveFile>();
            _backup = std::make_shared<HandleBackUp>();

            // 连接
            _save->SetNext(_backup);

            // 避免子进程僵尸
            signal(SIGCHLD, [](int signum)
                   {
                   int status = 0;
                   pid_t id = 0;
                   while ((id = (waitpid(-1, &status, WNOHANG))) > 0)
                   {
                       if (WIFEXITED(status))
                       {
                           // 父进程删除文件
                           DLOG("要删除的文件:%s\n", delfile.c_str());
                           std::cout << std::filesystem::remove(delfile) << std::endl;
                       }
                       else if (WIFSIGNALED(status))
                       {
                           ELOG("子进程%d被信号 %d 终止\n", id,WTERMSIG(status));
                       }
                   } });
        }

        void Run(std::string &text)
        {
            _save->Execute(text);
        }
    };
    class MsgQueue
    {
    private:
        int _msgfd;
        struct msgbuf
        {
            long mtype;
            char mtext[default_size];
        };

    public:
        MsgQueue() : _msgfd(default_fd)
        {
        }
        // 创建消息队列
        void Create(int flag)
        {
            // 1.先获取唯一key
            key_t key = ::ftok(PATHNAME, PROID);
            if (key == -1)
            {
                ELOG("ftok错误,error : %s\n", strerror(errno));
                abort();
            }
            // 2.创建消息队列
            _msgfd = msgget(key, flag);
            if (_msgfd == -1)
            {
                ELOG("创建消息队列失败\n");
                abort();
            }
            DLOG("创建消息队列完毕\n");
        }
        // 发送消息
        void Send(int type, const std::string &in)
        {
            struct msgbuf buf;
            memset(&buf, 0, sizeof(buf));
            // 1.拷贝
            buf.mtype = type;
            memcpy(buf.mtext, in.c_str(), in.size());
            // 2.发送
            int n = msgsnd(_msgfd, &buf, sizeof(buf.mtext), 0);
            if (n == -1)
            {
                ELOG("发送消息失败\n");
                return;
            }
            DLOG("发送消息完毕\n");
        }
        // 读取数据
        void Recv(int type, std::string *out)
        {
            struct msgbuf buf;

            int n = msgrcv(_msgfd, &buf, sizeof(buf.mtext), type, 0);
            if (n == -1)
            {
                ELOG("接收消息错误\n");
                return;
            }

            buf.mtext[n] = 0;
            *out = buf.mtext;
            DLOG("接收消息完毕\n");
        }
        // 获取消息队列属性
        void GetAttribute(struct msqid_ds *out)
        {
            struct msqid_ds att;
            memset(&att, 0, sizeof(att));

            int n = ::msgctl(_msgfd, IPC_STAT, &att);
            if (n == -1)
            {
                ELOG("获取消息队列属性失败\n");
                return;
            }
            *out = att;
            return;
        }
        void Destroy()
        {
            int n = msgctl(_msgfd, IPC_RMID, 0);
            if (n == -1)
            {
                ELOG("销毁消息队列失败\n");
                abort();
            }
            DLOG("销毁消息队列完毕\n");
        }
        ~MsgQueue()
        {
        }
    };

    class Server : public MsgQueue
    {
    public:
        Server()
        {
            MsgQueue::Create(CREATEMSGQUEUE);
            DLOG("服务端创建完毕\n");
        }
        ~Server()
        {
            MsgQueue::Destroy();
        }
    };

    class Client : public MsgQueue
    {

    public:
        Client()
        {
            MsgQueue::Create(GETMSGQUEUE);
            DLOG("客户端创建完毕\n");
        }
        ~Client()
        {
        }
    };
    enum 
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL,
        _SCREEN_TYPE_,
        _FILE_TYPE
    };
    struct Logmessage
    {
        std::string _level; // 等级
        pid_t _pid;
        std::string _filename;   // 文件名
        int _filenumber;         // 行号
        std::string _curr_time;  // 获取日志信息出现的时间
        std::string _logmessage; // 信息
    };

    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
            break;
        case INFO:
            return "INFO";
            break;
        case WARNING:
            return "WARNING";
            break;
        case ERROR:
            return "ERROR";
            break;
        case FATAL:
            return "FATAL";
            break;
        default:
            return "UNKNOW";
            break;
        }
    }
    std::string GetCurrTime()
    {
        time_t now_time = time(nullptr);
        struct tm *curr_time = localtime(&now_time);
        // 转换为string格式
        char buf[128];
        snprintf(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d",
                 curr_time->tm_year + 1900, curr_time->tm_mon + 1, curr_time->tm_mday, curr_time->tm_hour, curr_time->tm_min, curr_time->tm_sec);
        return buf;
    }
    class Log
    {
        void LogMsgToScreen(const Logmessage &logmsg)
        {
            printf("[%s][%d][%s][%d][%s]: %s",
                   logmsg._level.c_str(),
                   logmsg._pid, logmsg._filename.c_str(),
                   logmsg._filenumber,
                   logmsg._curr_time.c_str(),
                   logmsg._logmessage.c_str());
        }
        void LogMsgToFile(const Logmessage &logmsg)
        {
            char buf_info[2048] = "\0";
            snprintf(buf_info, sizeof(buf_info), "[%s][%d][%s][%d][%s] %s",
                     logmsg._level.c_str(),
                     logmsg._pid, logmsg._filename.c_str(),
                     logmsg._filenumber, logmsg._curr_time.c_str(),
                     logmsg._logmessage.c_str());
            // 格式化完毕,发送给服务端责任链处理
            _client.Send(CLIENT_MSG_TYPE, buf_info);
        }
        void FlushLogMsg(int level, const Logmessage &logmsg)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            switch (_type)
            {
            case _SCREEN_TYPE_:
                LogMsgToScreen(logmsg);
                break;
            case _FILE_TYPE:
                LogMsgToFile(logmsg);
                break;
            }
        }
        Log(const std::string &logfile = default_filepath + default_filename) : _logfile(logfile), _type(_SCREEN_TYPE_)
        {
        }

    public:
        Log(const Log &lg) = delete;
        Log(Log &lg) = delete;
        Log(Log &&lg) = delete;
        void ModPrintFormat(int type)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _type = type;
        }
        void LogMessage(int level, std::string filename, int filenumber, const char *format, ...) // 注意-->可变函数参数
        {
            if (level == _flag && _isopen)
                return;
            Logmessage msg;
            msg._level = LevelToString(level);
            msg._filename = filename;
            msg._pid = getpid();
            msg._filenumber = filenumber;
            msg._curr_time = GetCurrTime();
            // 注意：取出可变参数的固定写法
            va_list _ap;           // 创建变量,本质是一个指针
            va_start(_ap, format); // 将参数列表中离...最近的确定的参数传入
            char log_info[512];
            vsnprintf(log_info, sizeof(log_info), format, _ap);
            va_end(_ap); // 销毁_ap
            msg._logmessage = log_info;
            FlushLogMsg(level, msg);
        }
        ~Log()
        {
        }
        void EnableFiltration(int flag)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _isopen = true;
            _flag = flag;
        }
        void DisableFiltration()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _isopen = false;
        }
        static Log *GetInstance(const std::string &logfile = default_filepath + default_filename)
        {
            // 双重检查锁定模式
            if (_lg == nullptr)
            {
                std::unique_lock<std::mutex> lock(_static_mtx);
                if (_lg == nullptr)
                {
                    _lg = new Log(logfile);
                }
            }
            return _lg;
        }

    private:
        static Log *_lg;
        int _type;
        std::string _logfile;
        std::mutex _mtx;
        static std::mutex _static_mtx;
        bool _isopen = false;
        int _flag = 0;
        Client _client;
    };

    std::mutex Log::_static_mtx;
    Log *Log::_lg = nullptr;
    static std::unique_ptr<Log> _ptr(Log::GetInstance());
// 打开过滤器--设置过滤级别
#define ENABLE_FILTRATION(flag)       \
    do                                \
    {                                 \
        _ptr->EnableFiltration(flag); \
    } while (0)
// 关闭过滤器
#define ClOSE_FILTRATION           \
    do                             \
    {                              \
        _ptr->DisableFiltration(); \
    } while (0)
#define LOG(level, format, ...)                                             \
    do                                                                      \
    {                                                                       \
        _ptr->LogMessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0)

#define LOG_TO_SCREEN()                      \
    do                                       \
    {                                        \
        _ptr->ModPrintFormat(_SCREEN_TYPE_); \
    } while (0)

#define LOG_TO_FILE()                     \
    do                                    \
    {                                     \
        _ptr->ModPrintFormat(_FILE_TYPE); \
    } while (0)
}